using System;
using System.Collections;
using Microsoft.SPOT;

namespace Soigeneris
{
    public sealed class TestConfig
    {

        static ArrayList ConnectorList; // 
        static ArrayList TestList;

        #region Helper_Structs

        /// <summary>
        /// The Connector struct encapsulates the information we need to know
        /// about a phyiscal connector, its physical pins and the TestPoints
        /// those pins are mapped to.
        /// </summary>
        /// A connector MUST have a pin mapping for
        /// for each pin, that way we can test for shorts to non-uses pins.
        /// 
        /// Remember - TestPoints are the physical connection points we have
        /// available on the test rig. The TestPoint may correspond to an I/O
        /// pin on the FEZ or it might be a pin on a MUX chip that the FEZ reads.
        /// 
        /// Definitions -
        /// Connector: An actual physical connector housing one or more pins.
        /// Pin: A physical pin belonging to the connector
        /// TestPoint: An electrical test connection on the test rig.
        public struct Connector
        {
            String _Name;             // name of connector, i.e. C1
            int _pinCount;            // # of physical pins
            TestPoint[] _Pin2Point; // _Pin2Point[PhysicalPin - 1] TestPoint mapped to pin

            /// <summary>
            /// Convient constructor
            /// </summary>
            /// <param name="name">Name of connector</param>
            /// <param name="numPins"># of physical pins</param>
            public Connector(String name, int numPins)
            {
                _Name = name;
                _Pin2Point = new TestPoint[numPins];
                _pinCount = numPins;
            }

            /// <summary>
            /// Property - Number of physical pins
            /// </summary>
            public int PinCount
            {
                get { return _pinCount; }
                set { _pinCount = value; }
            }

            /// <summary>
            /// Add a Physical Pin / TestPoint pair to this connector
            /// </summary>
            /// <param name="pin">Physical Pin</param>
            /// <param name="testPoint">TestPoint</param>
            public void Add(int pin, TestPoint testPoint)
            {
                _Pin2Point[pin - 1] = testPoint;
            }

            /// <summary>
            /// Retrive TestPoint Mapped to this pin.
            /// </summary>
            /// <param name="pin">Physical Pin on this connector</param>
            /// <returns>TestPoint or Zero if a mapping does not exist</returns>
            public TestPoint Pin2Point(int pin)
            {
                TestPoint point = 0;
                if (pin < _Pin2Point.Length + 1)
                {
                    point = _Pin2Point[pin - 1]; // returns TestPoint
                }
                return point;
            }

            /// <summary>
            ///  Does this conenctor use given TestPoint?
            /// </summary>
            /// <param name="testPoint"></param>
            /// <returns>pin # if used, otherwise -1</returns>
            public int UsesPoint(TestPoint testPoint)
            {
                for (int i = 0; i < _pinCount; i++)
                {
                    if (_Pin2Point[i] == testPoint) { return i + 1; }
                }
                return -1;
            }

            /// <summary>
            /// Retrive name of this test
            /// </summary>
            public String Name
            {
                get { return _Name; }
            }

        }

        /// <summary>
        /// A connector/pin pair that is used to make it convienant
        /// to pass this inforamtion around.
        /// Name == Connector name
        /// Pin == Connectors phisical pin
        /// </summary>
        public struct ConnectorPinMap
        {
            int _Pin;          // Connector's physical pin
            String _Connector; // Connector's name

            /// <summary>
            /// Convienant constructor
            /// </summary>
            /// <param name="connectorName">Name of connector, i.e. 'C1'</param>
            /// <param name="pin">Physical pin number</param>
            public ConnectorPinMap(String connectorName, int pin)
            {
                _Pin = pin;
                _Connector = connectorName;
            }

            /// <summary>
            /// Returns physical pin number
            /// </summary>
            public int Pin
            {
                get { return _Pin; }
            }

            /// <summary>
            /// Returns the connector name, i.e.'C1'
            /// </summary>
            public String ConnectorName
            {
                get { return _Connector; }
            }
        }

        /// <summary>
        ///  The Test struct encapsulates the information to conduct a test.
        ///  We keep track of the name of the test the TestPoint that is driven, 
        ///  i.e. _drivePoint, an array of driven TestPoints that should have
        ///  electrical connection to the driven point.
        /// </summary>
        public struct Test
        {
            String _Name;            // name of test
            TestPoint _drivePoint;         // the TestPoint that is driven for this test
            ArrayList _drivenPoints; // TestPoints (ints) electrically connected
                                     // driven point.

            /// <summary>
            ///  Convienant constructor
            /// </summary>
            /// <param name="name">Name of test, i.e. 'Test 1'</param>
            public Test(String name)
            {
                _Name = name;
                _drivePoint = 0;
                _drivenPoints = new ArrayList();
            }

            /// <summary>
            /// Access to set get name, i.e. 1Test 1'
            /// </summary>
            public String Name
            {
                get { return _Name; }
                set { _Name = value; }
            }

            /// <summary>
            ///  Helper method for accessing Drive Pin
            ///  ConnectorPinmap
            /// </summary>
            public ConnectorPinMap DrivePin
            {
                // we need to use 'TestConfig.Instance' to get an object
                // reference to the TestConfig call in order to call one
                // of its methods. Since TestConfig is a singleton it works.
                get 
                { 
                    return TestConfig.Instance.Point2PinMap(_drivePoint); 
                } 
                set
                {
                    _drivePoint = TestConfig.Instance.GetConnector(value.ConnectorName).Pin2Point(value.Pin);
                }
            }

            /// <summary>
            /// Helper method for accessing Drive Pin
            /// For convienace we return this as a TestPoint
            /// </summary>
            /// <returns>Drive TestPoint</returns>
            public TestPoint GetDrivePoint()
            {
                return _drivePoint;
            }

            /// <summary>
            /// Helper to add driven pins
            /// </summary>
            /// <param name="map">Connector/Pin pair to add to list</param>
            /// <returns>True == Success</returns>
            public bool AddDrivenPin(ConnectorPinMap map)
            {
                // we need to use 'TestConfig.Instance' to get an object
                // reference to the TestConfig call in order to call one
                // of its methods. Since TestConfig is a singleton it works.
                Connector con = TestConfig.Instance.GetConnector(map.ConnectorName);
                bool success = false;
                TestPoint point = con.Pin2Point(map.Pin);
                if (point > 0)
                {
                    _drivenPoints.Add(point);
                    success = true;
                }
                return success;
            }

            /// <summary>
            ///  Returns # of driven TestPoints in this test
            /// </summary>
            public int DrivenPinCount
            {
                get { return _drivenPoints.Count; }
            }

            /// <summary>
            ///  Return TestPoint from list at given index 
            /// </summary>
            /// <param name="index">Index of TestPoint in list</param>
            /// <returns>Indexed TestPoint, or 0 if error</returns>
            public TestPoint GetTestPoint(int index)
            {
                TestPoint point = TestPoint.None;
                if (index < DrivenPinCount)
                {
                    point = (TestPoint)_drivenPoints[index];
                }
                return point;
            }

        }

        #endregion

        #region TestConfig_Class

        ///////////////////////////////////////////////////////////////////////
        //
        // TestConfig Class
        //
        ///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// A bit of gibberish to make this a singleton class, not thread safe.
        /// Call the static method 'Intance' to instantiate the class.
        /// </summary>
        private static readonly TestConfig instance = new TestConfig();

        /// <summary>
        /// Returns a reference to current instance (if any)
        /// or creates new instance if one does not exists.
        /// This is how you instantaite the class!
        /// </summary>
        public static TestConfig Instance
        {
            get { return instance; }
        }

        /// <summary>
        /// This constructor is hidden, must use TestConfig.Instance
        /// </summary>
        private TestConfig()
        {
            ConnectorList = new ArrayList();
            TestList = new ArrayList();
        }

        /// <summary>
        /// Returns # connectors we have
        /// </summary>
        public int ConnectorCount
        {
            get { return ConnectorList.Count; }
        }

        /// <summary>
        /// Add a new connector to the ConnectorList
        /// </summary>
        /// <param name="con">Connector to add</param>
        public void AddConnector(Connector con)
        {
            ConnectorList.Add(con);
        }

        /// <summary>
        /// Access a connector by its index in list
        /// </summary>
        /// <param name="index">Cnnector index in list</param>
        /// <returns>Connector at index given, or null Connector if index invalid</returns>
        public Connector GetConnector(int index)
        {
            if (index < ConnectorList.Count)
            {
                return (Connector)ConnectorList[index];
            }
            return new Connector();
        }

        /// <summary>
        /// Returns a reference to a Connector of given name.
        /// </summary>
        /// <param name="name">Name of Connector to find</param>
        /// <returns>Connector of name given, or null connector if not found</returns>
        public Connector GetConnector(String name)
        {
            for (int i = 0; i < ConnectorCount; i++)
            {
                if (GetConnector(i).Name == name)
                {
                    return (Connector)ConnectorList[i];
                }
            }
            return new Connector();
        }

        /// <summary>
        /// Returns number of tests defined
        /// </summary>
        public int TestCount
        {
            get { return TestList.Count; }
        }

        /// <summary>
        /// Add a new test to the list.
        /// </summary>
        /// <param name="test"></param>
        public void AddTest(Test test)
        {
            TestList.Add(test);
        }

        /// <summary>
        /// Access text at given index
        /// </summary>
        /// <param name="index">Index of test in list</param>
        /// <returns>Test at location index, null Test if not found.</returns>
        public Test GetTest(int index)
        {
            if (index < TestList.Count)
            {
                return (Test)TestList[index];
            }
            return new Test();
        }

        /// <summary>
        /// Retrive the PinMap assocated with a TestPoint
        /// </summary>
        /// <param name="TestPoint">The TestPoint</param>
        public ConnectorPinMap Point2PinMap(TestPoint testPoint)
        {
            int pin;

            for (int i = 0; i < ConnectorList.Count; i++)
            {
                Connector con = (Connector)ConnectorList[i];
                pin = con.UsesPoint(testPoint);
                if (pin > -1) { return new ConnectorPinMap(con.Name, pin); }
            }

            return new ConnectorPinMap();
        }
    }

        #endregion
}
